#region Imported namespaces
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.Design;
using System.Web.UI.WebControls;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Collections;
#endregion

namespace PHSRAG.WebControls
{
	#region Class BreadCrumb 
	/// <summary>
	/// The control represents page's breadcrumb trail. It has a public
	/// method used by pages to add themselves to the breadcrumb trail.
	/// </summary>
	[ToolboxData("<{0}:BreadCrumb runat=server></{0}:BreadCrumb>"), Designer(typeof(BreadCrumbDesigner))]
	public class BreadCrumb : System.Web.UI.WebControls.WebControl, INamingContainer
	{
		#region Constants
		private const string BreadCrumbKey = "BreadCrumb_2E2BDD3B-A640-4f8e-B695-56927A210606";
		private const string BreadCrumbSeparator = "&nbsp;&gt;&gt;&nbsp;";
		private const string NavigateCommand = "Navigate";
		#endregion

		#region Variables
		private ArrayList breadCrumbs;
		private bool highlightCurrentPage;
		#endregion

		#region Public Property
		/// <summary>
		/// Gets the collection of breadcrumbInfo object
		/// </summary>
		public ArrayList BreadCrumbDetails
		{
			get { return breadCrumbs;}
		}

		public bool HighlightCurrentPage
		{
			set { highlightCurrentPage = value; }
		}
		#endregion

		#region Events
		/// <summary>
		/// Navigation Event to be handled by containing page.
		/// </summary>
		public event CommandEventHandler Navigate 
		{
			add		{ Events.AddHandler(NavigateCommand, value); }
			remove	{ Events.RemoveHandler(NavigateCommand, value); }
		}
		#endregion

		#region Overrides
		/// <summary>
		/// Setup the control tree to reflect the layout of the controls within the BreadCrumb.
		/// </summary>
		protected override void CreateChildControls()
		{
			Controls.Clear();

			Table table = new Table();
			Controls.Add(table);
			table.CssClass = "BreadCrumb";
			table.CellSpacing = 10;
			table.CellPadding = 10;

			TableRow row = new TableRow();
			table.Rows.Add(row);

			TableCell cell = new TableCell();
			row.Cells.Add(cell);
			cell.Controls.Add(new LiteralControl("&nbsp;"));

			if (IsDesignMode)
			{
				cell.Controls.Add(new LiteralControl("BreadCrumb"));
			}
			else
			{
				if (breadCrumbs == null) 
				{
					breadCrumbs = Page.Session[BreadCrumbKey] as ArrayList;
					if (breadCrumbs == null)
						throw new Exception("The breadcrumb for this page was not initialized"); 
				}

				for (int index = 0; index < breadCrumbs.Count; ++index) 
				{
					BreadCrumbInfo info = (BreadCrumbInfo)breadCrumbs[index];
					if (index != breadCrumbs.Count - 1)
					{
						LinkButton breadCrumbLink = new LinkButton();
						cell.Controls.Add(breadCrumbLink);
						breadCrumbLink.Text = info.Text;
						breadCrumbLink.CommandArgument = info.NavigateUrl;
						breadCrumbLink.CommandName = Convert.ToString(info.Level);
						breadCrumbLink.CausesValidation = false;
						cell.Controls.Add(new LiteralControl(BreadCrumbSeparator));
					}
					else 
					{
						Label breadCrumbLabel = new Label();
						if (highlightCurrentPage)
							breadCrumbLabel.CssClass = "BreadCrumbCurrentPage";
						cell.Controls.Add(breadCrumbLabel);
						breadCrumbLabel.Text = info.Text;
					}				
				}
			}
		}

		/// <summary>
		/// Invoked when an event is raised by a child control.  Notifies the handler that a Navigation
		/// is occurring.
		/// </summary>
		/// <param name="source">Reference to the child control that raised the event</param>
		/// <param name="e">Argument associated with the event</param>
		/// <returns>True if the event was handle by this method; false otherwise</returns>
		protected override bool OnBubbleEvent(object source, EventArgs e)
		{
			CommandEventHandler handler = (CommandEventHandler)Events[NavigateCommand];
				try
				{
					if (handler != null)
						handler(this, new CommandEventArgs(null, e));
					Page.Response.Redirect(((LinkButton)source).CommandArgument);
				}
				catch
				{
					// if the page doesn't want to redirect, it can throw an exception and the 
					// breadcrumb won't redirect
				}

			return true;
		}

		/// <summary>
		/// Disable view state.
		/// </summary>
		/// <param name="e">Argument associated with the event</param>
		protected override void OnLoad(EventArgs e)
		{
			base.OnLoad(e);
			EnableViewState = false;
		}
		#endregion

		#region Private Methods
		/// <summary>
		/// Gets the index of the BreadCrumb in the Collection.
		/// Matching is done on levels since no two pages with
		/// the same level will ever exist in the breadcrumb.
		/// </summary>
		/// <param name="breadCrumbInfo">The BreadCrumb whose index is to be returned.</param>
		/// <returns>The index of the breadcrumb if found, -1 otherwise.</returns>
		private int GetBreadCrumbIndex(BreadCrumbInfo breadCrumbInfo) 
		{
			int breadCrumbIndex = -1;
			for (int index = 0; index < breadCrumbs.Count; ++index) 
			{
				if (((BreadCrumbInfo)breadCrumbs[index]).Level == breadCrumbInfo.Level)
				{
					breadCrumbIndex = index;
					break;
				}
			}
			return breadCrumbIndex;
		}
		#endregion

		#region Public Methods
		/// <summary>
		/// A method to be called by pages that want to be a part of the BreadCrumb trail.
		/// It adds a new breadcrumb.
		/// </summary>
		/// <param name="text">The text to be displayed on the breadcrumb trail.</param>
		/// <param name="navigateUrl">The url to navigate to once the text is clicked.</param>
		/// <param name="level">The page level.</param>
		public void Add (string text, string navigateUrl, int level)
		{
			BreadCrumbInfo breadCrumbInfo = new BreadCrumbInfo(text, navigateUrl, level);
			breadCrumbs = Page.Session[BreadCrumbKey] as ArrayList;
			if (breadCrumbs != null)
			{
				if (((BreadCrumbInfo)breadCrumbs[breadCrumbs.Count - 1]).Level == level) 
				{
					breadCrumbs.RemoveAt(breadCrumbs.Count - 1);
					breadCrumbs.Add(breadCrumbInfo);
				}
				else 
				{
					int breadCrumbIndex = GetBreadCrumbIndex(breadCrumbInfo);
					if (breadCrumbIndex == 0)
					{
						breadCrumbs.Clear();
					}
					else if (breadCrumbIndex > 0)
					{
						breadCrumbs.RemoveRange(breadCrumbIndex, breadCrumbs.Count - breadCrumbIndex);
					}
					breadCrumbs.Add(breadCrumbInfo);
				}
			}
			else 
			{
				breadCrumbs = new ArrayList();
				breadCrumbs.Add(breadCrumbInfo);
			}
			Page.Session[BreadCrumbKey] = breadCrumbs;
		}
		#endregion

		#region Private Properties
		/// <summary>
		/// Determine if the control is in design mode. It is if there is not HTTP context.
		/// </summary>
		private static bool IsDesignMode
		{
			get { return HttpContext.Current == null; }
		}
		#endregion

		#region Internal Methods
		/// <summary>
		/// This method is ONLY invoked by the associated designer to show the control contents at design time.
		/// </summary>
		internal void Design()
		{
			EnsureChildControls();
		}
		#endregion
	}
	#endregion

	#region Struct BreadCrumbInfo
	/// <summary>
	/// The struct encapsulates the information about a breadcrumb.
	/// It also exposes public properties to set and get the breadcrumb
	/// info.
	/// </summary>
	public struct BreadCrumbInfo
	{
		#region Variables
		private string text;
		private string navigateUrl;
		private int level;        
		#endregion

		#region Properties
		/// <summary>
		/// Gets the text associated with the Breadcrumb.
		/// </summary>
		public string Text 
		{
			get { return text; }
			set { text = value; }
		}
		/// <summary>
		/// Gets or Sets the url to navigate to when breadcrumb is clicked.
		/// </summary>
		public string NavigateUrl 
		{
			get { return navigateUrl; }
			set { navigateUrl = value; }
		}
		/// <summary>
		/// Gets or Sets the breadcrumb level.
		/// </summary>
		public int Level
		{
			get { return level; }
			set { level = value; }
		}
		#endregion

		#region Constructor
		/// <summary>
		/// Initializes breadcrumb members.
		/// </summary>
		/// <param name="text">Text to display in the breadcrumb.</param>
		/// <param name="navigateUrl">The url to navigate to when breadcrumb is clicked.</param>
		/// <param name="level">The breadcrumb level</param>
		public BreadCrumbInfo(string text, string navigateUrl, int level) 
		{
			this.text = text;
			this.navigateUrl = navigateUrl;
			this.level = level;
		}
		#endregion
	}
	#endregion

	#region Class BreadCrumbDesigner
	/// <summary>
	/// Designer associated with the BreadCrumb control.
	/// </summary>
	/// <remarks>
	/// This class cannot be used directly.
	/// </remarks>
	public class BreadCrumbDesigner : ControlDesigner
	{
		#region Overrides
		/// <summary>
		/// Force the control to create its child controls for display at design time.
		/// </summary>
		/// <returns>HTML representation of the BreadCrumb control</returns>
		public override string GetDesignTimeHtml()
		{
			((BreadCrumb)Component).Design();
			return base.GetDesignTimeHtml();
		}
		#endregion
	}
	#endregion
}
